package baseFx.common.context.spring;

import baseFx.common.Action1T;
import baseFx.common.EventDispatcher;
import baseFx.common.context.BeanDef;
import baseFx.common.context.TypeResolver;
import baseFx.common.utils.ClassUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.GenericApplicationContext;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class SpringApplicationContext extends GenericApplicationContext implements FxApplicationContext {
    private static String eventFireKey = UUID.randomUUID().toString();
    public final EventDispatcher<RegisterBeanEventArgs> onRegisterBean = new EventDispatcher<>(eventFireKey);
    private SpringContextExtend extend;

    public SpringApplicationContext() {
        this(null);
    }

    public SpringApplicationContext(ApplicationContext parent, BeanDef... defs) {
        setParent(parent);
        extend = new SpringContextExtend(this);
        extend.setRegisterParam(defs);
    }

    @Override
    public void addDefaultScope(String target, String scope) {
        extend.registerDefaultScope(new DefaultScopeSetting(target, scope));
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
        RegisterBeanEventArgs args = new RegisterBeanEventArgs(beanName, beanDefinition);
        onRegisterBean.fire(eventFireKey, this, args);
        if (args.isCancel) {
            return;
        }
        extend.setDefaultScope(beanName, args.define);
        super.registerBeanDefinition(beanName, args.define);
    }

    @Override
    public TypeResolver root() {
        ApplicationContext parent = this;
        while (parent.getParent() instanceof TypeResolver) {
            parent = parent.getParent();
        }
        return (TypeResolver) parent;
    }

    @Override
    public SpringApplicationContext sub(BeanDef... defs) {
        SpringApplicationContext context = new SpringApplicationContext(this, defs);
        extend.addChild(context);
        context.refresh();
        return context;
    }

    @Override
    public void register(BeanDef def) {
        extend.setRegisterParam(def);
    }

    @Override
    public <T> T resolve(Class<T> clazz) {
        return getBean(clazz);
    }

    @Override
    public List<Class<?>> getClassListForType(Class<?> clazz) {
        List<Class<?>> list = new ArrayList<>();
        String[] names = getBeanNamesForType(clazz);
        if (names != null && names.length > 0) {
            for (String name : names) {
                BeanDefinition def = getBeanDefinition(name);
                list.add(ClassUtils.getClassForName(def.getBeanClassName()));
            }
        }
        return list;
    }

    @Override
    public void scanPackage(Action1T<ClassPathBeanDefinitionScanner> action, String... pkg) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return beanDefinition.getMetadata().isIndependent();
            }
        };
        if (action != null) {
            action.execute(scanner);
        }
        scanner.scan(pkg);
    }

    //todo BeanNames相关需要一堆类似的重载，暂时就写了这一个要用到的

    @Override
    public String[] getBeanNamesForType(Class<?> type) {
        String[] names = super.getBeanNamesForType(type);
        if (names.length == 0 && getParent() != null && getParent() != this) {
            names = getParent().getBeanNamesForType(type);
        }
        return names;
    }
}
